rr library(fable) library(sweep)
Registered S3 method overwritten by 'xts':
method from
as.zoo.xts zoo
The fable package is a tidy renovation of the forecast package, and it explores new interfaces for modelling and subsequent analysis in R. For users experienced with the tidyverse, modelling in R can be a jarring experience. Models in R can be difficult to work with as there is little standardisation in model object structures and interfaces. This is partially alleviated from tidy modelling packages such as broom (and sweep for time series) which can be used to extract key features from model objects in suitable formats for tidy workflows.
the fable package applies tidyverse principles to time series modelling, making the forecasting workflow seamlessly integrate with other tidyverse packages.
rr library(fable) library(tsibble) library(tsibbledata) library(lubridate) library(dplyr)
The process of producing forecasts for time series data can be broken down into a few steps.
Data Prep
The first step in forecasting is to prepare data in the correct format. This process may involve loading in data, identifying missing values, filtering the time series and other pre-processing tasks. The functionality provided by tsibble and other packages in the tidyverse substantially simplifies this step.
Many models have different data requirements, some require the series to be in time order, others require no missing values. Checking your data is an essential step to understanding its features and it is useful to do before models are estimated.
The way in which your data is prepared can also be used to explore different features of the time series. As we will see later, pre-processing your dataset is an important step in evaluating model performance using cross-validation.
rr global_economy
Visualise
rr global_economy %>% filter(Country==) %>% autoplot(GDP) + ggtitle(for Sweden) + ylab($US billions)

Define a model
Before fitting a model to the data, we first must describe the model. There are many different time series models that can be used for forecasting, and much of this book is dedicated to describing various models. Specifying an appropriate model for the data is essential for producing appropriate forecasts.
Models in R are specified using model functions, which each use a formula (y ~ x) interface. The response variable(s) are specified on the left of the formula, and the structure of the model is written on the right.
For example, a time series linear model that models GDP can be specified using:
TSLM(GDP ~ trend()).
In this case the model function is TSLM() (time series linear model), the response variable is GDP and it is being modelled using trend() (a “special” function specifying a linear trend).
Train the model
Once an appropriate model is specified, we next train the model on some data. One or more model specifications can be estimated using the model() function.
To estimate the model in our example, we use:
rr fit <- global_economy %>% model(trend_model = TSLM(GDP ~ trend()))
7 errors (1 unique) encountered for trend_model
[7] 0 (non-NA) cases
This fits a linear trend model to the GDP data for each combination of key variables in the tsibble. In this example, it will fit a model to each of the 263 countries in the dataset. The resulting object is a model table or a “mable”.
rr fit
Each row corresponds to one combination of the key variables. The trend_model column contains information about the fitted model for each country.
Check/evaluate your model
Once a model has been fitted, it is important to check how well it has performed on the data. There are several diagnostic tools available to check model behaviour, and also accuracy measures that allow one model to be compared against another.
Produce forecasts
With an appropriate model specified, estimated and checked, it is time to produce the forecasts using forecast(). The easiest way to use this function is by specifying the number of future observations to forecast. For example, forecasts for the next 10 observations can be generated using h = 10. We can also use natural language; e.g., h = “2 years” can be used to predict two years into the future.
rr fit %>% forecast(h = years)
Could not bias adjust the point forecasts as the forecast standard deviation is unknown. Perhaps your series is too short or insufficient bootstrap samples are used.
This is a forecasting table, or “fable”. Each row corresponds to one forecast period for each country. The GDP column contains the point forecast, while the .distribution column contains the forecasting distribution. The point forecast is the mean (or average) of the forecasting distribution.
The forecasts can be plotted along with the historical data using autoplot() as follows.
rr fit %>% forecast(h = years) %>% filter(Country==) %>% autoplot(global_economy) + ggtitle(for Sweden) + ylab($US billions)
Could not bias adjust the point forecasts as the forecast standard deviation is unknown. Perhaps your series is too short or insufficient bootstrap samples are used.

Forecasting methods
- Average method
Here, the forecasts of all future values are equal to the average (or “mean”) of the historical data.
rr global_economy %>% model(MEAN(GDP)) %>% autoplot()
7 errors (1 unique) encountered for MEAN(GDP)
[7] All observations are missing, a model cannot be estimated without data.
Error: Objects of type mdl_df/tbl_df/tbl/data.frame not supported by autoplot.
- Naive method
For naïve forecasts, we simply set all forecasts to be the value of the last observation. These are also known as random walk forecasts, as this method is optimal when data follows a random walk model. This method works remarkably well for many economic and financial time series.
rr global_economy
Error in global_economy(.) : could not find function \global_economy\
- Seasonal naive method
A similar method is useful for highly seasonal data. In this case, we set each forecast to be equal to the last observed value from the same season of the year (e.g., the same month of the previous year).
This looks more complicated than it really is. For example, with monthly data, the forecast for all future February values is equal to the last observed February value. With quarterly data, the forecast of all future Q2 values is equal to the last observed Q2 value (where Q2 means the second quarter). Similar rules apply for other months and quarters, and for other seasonal periods.
rr global_economy %>% global_economy %>% model(SNAIVE(GDP ~ lag()))
Error in global_economy(.) : could not find function \global_economy\
rr model(SNAIVE(GDP ~ lag()))
Error in UseMethod(\model\) :
no applicable method for 'model' applied to an object of class \c('mdl_defn'
- Drift method
A variation on the naïve method is to allow the forecasts to increase or decrease over time, where the amount of change over time (called the drift) is set to be the average change seen in the historical data. This is equivalent to drawing a line between the first and last observations, and extrapolating it into the future.
rr global_economy %>% model(RW(GDP ~ drift()))
|=========== | 38% ~31 s remaining
|============= | 42% ~27 s remaining
|============== | 46% ~23 s remaining
|============== | 47% ~22 s remaining
|================ | 52% ~18 s remaining
|================= | 57% ~15 s remaining
|=================== | 62% ~12 s remaining
|===================== | 68% ~9 s remaining
|====================== | 73% ~7 s remaining
|======================= | 77% ~6 s remaining
|======================== | 80% ~5 s remaining
|========================== | 84% ~4 s remaining
|========================== | 86% ~3 s remaining
|============================ | 90% ~2 s remaining
|============================ | 93% ~1 s remaining
|============================== | 99% ~0 s remaining
7 errors (1 unique) encountered for RW(GDP ~ drift())
[7] All observations are missing, a model cannot be estimated without data.
cannot open compressed file '/Users/stephanieboyle/.rstudio-desktop/notebooks/F8199C2F-time_series_forecasting/1/A01BA05017392E95/cwri0ao840vm5_t/_rs_rdf_372a75dfc3df.rdf', probable reason 'No such file or directory'Error in gzfile(file, \wb\) : cannot open the connection
Now it’s your turn:
rr # Set training data from 1992 to 2006 train <- aus_production %>% filter_index(992 Q1 ~ 006 Q4) # Fit the models beer_fit <- train %>% model( Mean = MEAN(Beer), Naïve = NAIVE(Beer), Seasonal naïve = SNAIVE(Beer) ) # Generate forecasts for 14 quarters beer_fc <- beer_fit %>% forecast(h=14) # Plot forecasts against actual values beer_fc %>% autoplot(train, level = NULL) + autolayer(filter_index(aus_production, 007 Q1 ~ .), color = ) + ggtitle(for quarterly beer production) + xlab() + ylab() + guides(colour=guide_legend(title=))
In this case, only the seasonal naïve forecasts are close to the observed values from 2007 onwards.
rr
aus_retail1 <- aus_retail %>% filter(State %in% c(South Wales, ), Industry == stores)
aus_retail1 %>% model( ets = ETS(box_cox(Turnover, 0.3)), arima = ARIMA(log(Turnover)), snaive = SNAIVE(Turnover) ) %>% forecast(h = years) %>% autoplot(filter(aus_retail, year(Month) > 2010), level = NULL)
rr bricks <- aus_production %>% filter_index(1970 ~ 2004) bricks %>% model(MEAN(Bricks)) bricks %>% model(NAIVE(Bricks)) bricks %>% model(SNAIVE(Bricks ~ lag())) bricks %>% model(RW(Bricks ~ drift()))
the non-seasonal methods are applied to Google’s daily closing stock price in 2015, and used to forecast one month ahead. Because stock prices are not observed every day, we first set up a new time index based on the trading days rather than calendar days.
rr # Re-index based on trading days google_stock <- gafa_stock %>% filter(Symbol == ) %>% mutate(day = row_number()) %>% update_tsibble(index = day, regular = TRUE) # Filter the year of interest google_2015 <- google_stock %>% filter(year(Date) == 2015) # Fit the models google_fit <- google_2015 %>% model( Mean = MEAN(Close), Naïve = NAIVE(Close), Drift = NAIVE(Close ~ drift()) ) # Produce forecasts for the 19 trading days in January 2015 google_fc <- google_fit %>% forecast(h = 19) # A better way using a tsibble to determine the forecast horizons google_jan_2016 <- google_stock %>% filter(yearmonth(Date) == yearmonth(016 Jan)) google_fc <- google_fit %>% forecast(google_jan_2016) # Plot the forecasts google_fc %>% autoplot(google_2015, level = NULL) + autolayer(google_jan_2016, Close, color=‘black’) + ggtitle(stock (daily ending 31 Dec 2015)) + xlab() + ylab(Price (US$)) + guides(colour=guide_legend(title=))
Sometimes one of these simple methods will be the best forecasting method available; but in many cases, these methods will serve as benchmarks rather than the method of choice. That is, any forecasting methods we develop will be compared to these simple methods to ensure that the new method is better than these simple alternatives. If not, the new method is not worth considering.
ACF and PACF plots
We can also see how seasonal our data is by looking at ACF and PACF plots.
# acf plots
holidays %>%
ACF(Trips) %>%
autoplot()
holidays %>%
PACF(Trips,4) %>%
autoplot()
Here, the low seasonality in the ACT is evident compared to the other states.
Finally, we show a composite plot created using gg_tsdisplay(). This is a little different from the corresponding ggtsdisplay() function in the forecast package which showed the PACF in the bottom right panel by default. I think the season plot is a little more informative for exploratory data analysis, so that is what is shown by default in this new function. The other panels are the same.
holidays %>%
filter(State=="Tasmania") %>%
gg_tsdisplay(Trips)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CgpsaWJyYXJ5KGZhYmxlKQpsaWJyYXJ5KHN3ZWVwKQoKYGBgCgoKVGhlIGZhYmxlIHBhY2thZ2UgaXMgYSB0aWR5IHJlbm92YXRpb24gb2YgdGhlIGZvcmVjYXN0IHBhY2thZ2UsIGFuZCBpdCBleHBsb3JlcyBuZXcgaW50ZXJmYWNlcyBmb3IgbW9kZWxsaW5nIGFuZCBzdWJzZXF1ZW50IGFuYWx5c2lzIGluIFIuIEZvciB1c2VycyBleHBlcmllbmNlZCB3aXRoIHRoZSB0aWR5dmVyc2UsIG1vZGVsbGluZyBpbiBSIGNhbiBiZSBhIGphcnJpbmcgZXhwZXJpZW5jZS4gTW9kZWxzIGluIFIgY2FuIGJlIGRpZmZpY3VsdCB0byB3b3JrIHdpdGggYXMgdGhlcmUgaXMgbGl0dGxlIHN0YW5kYXJkaXNhdGlvbiBpbiBtb2RlbCBvYmplY3Qgc3RydWN0dXJlcyBhbmQgaW50ZXJmYWNlcy4gVGhpcyBpcyBwYXJ0aWFsbHkgYWxsZXZpYXRlZCBmcm9tIHRpZHkgbW9kZWxsaW5nIHBhY2thZ2VzIHN1Y2ggYXMgYnJvb20gKGFuZCBzd2VlcCBmb3IgdGltZSBzZXJpZXMpIHdoaWNoIGNhbiBiZSB1c2VkIHRvIGV4dHJhY3Qga2V5IGZlYXR1cmVzIGZyb20gbW9kZWwgb2JqZWN0cyBpbiBzdWl0YWJsZSBmb3JtYXRzIGZvciB0aWR5IHdvcmtmbG93cy4KCnRoZSBmYWJsZSBwYWNrYWdlIGFwcGxpZXMgdGlkeXZlcnNlIHByaW5jaXBsZXMgdG8gdGltZSBzZXJpZXMgbW9kZWxsaW5nLCBtYWtpbmcgdGhlIGZvcmVjYXN0aW5nIHdvcmtmbG93IHNlYW1sZXNzbHkgaW50ZWdyYXRlIHdpdGggb3RoZXIgdGlkeXZlcnNlIHBhY2thZ2VzLgoKYGBge3J9CmxpYnJhcnkoZmFibGUpCmxpYnJhcnkodHNpYmJsZSkKbGlicmFyeSh0c2liYmxlZGF0YSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZHBseXIpCmBgYAoKCgoKVGhlIHByb2Nlc3Mgb2YgcHJvZHVjaW5nIGZvcmVjYXN0cyBmb3IgdGltZSBzZXJpZXMgZGF0YSBjYW4gYmUgYnJva2VuIGRvd24gaW50byBhIGZldyBzdGVwcy4KCjxjZW50ZXI+CiFbXShodHRwczovL290ZXh0cy5jb20vZnBwMy9mcHBfZmlsZXMvZmlndXJlLWh0bWwvd29ya2Zsb3ctMS5wbmcpCjwvY2VudGVyPgoKCiMgRGF0YSBQcmVwCgpUaGUgZmlyc3Qgc3RlcCBpbiBmb3JlY2FzdGluZyBpcyB0byBwcmVwYXJlIGRhdGEgaW4gdGhlIGNvcnJlY3QgZm9ybWF0LiBUaGlzIHByb2Nlc3MgbWF5IGludm9sdmUgbG9hZGluZyBpbiBkYXRhLCBpZGVudGlmeWluZyBtaXNzaW5nIHZhbHVlcywgZmlsdGVyaW5nIHRoZSB0aW1lIHNlcmllcyBhbmQgb3RoZXIgcHJlLXByb2Nlc3NpbmcgdGFza3MuIFRoZSBmdW5jdGlvbmFsaXR5IHByb3ZpZGVkIGJ5IHRzaWJibGUgYW5kIG90aGVyIHBhY2thZ2VzIGluIHRoZSB0aWR5dmVyc2Ugc3Vic3RhbnRpYWxseSBzaW1wbGlmaWVzIHRoaXMgc3RlcC4KCk1hbnkgbW9kZWxzIGhhdmUgZGlmZmVyZW50IGRhdGEgcmVxdWlyZW1lbnRzLCBzb21lIHJlcXVpcmUgdGhlIHNlcmllcyB0byBiZSBpbiB0aW1lIG9yZGVyLCBvdGhlcnMgcmVxdWlyZSBubyBtaXNzaW5nIHZhbHVlcy4gQ2hlY2tpbmcgeW91ciBkYXRhIGlzIGFuIGVzc2VudGlhbCBzdGVwIHRvIHVuZGVyc3RhbmRpbmcgaXRzIGZlYXR1cmVzIGFuZCBpdCBpcyB1c2VmdWwgdG8gZG8gYmVmb3JlIG1vZGVscyBhcmUgZXN0aW1hdGVkLgoKVGhlIHdheSBpbiB3aGljaCB5b3VyIGRhdGEgaXMgcHJlcGFyZWQgY2FuIGFsc28gYmUgdXNlZCB0byBleHBsb3JlIGRpZmZlcmVudCBmZWF0dXJlcyBvZiB0aGUgdGltZSBzZXJpZXMuIEFzIHdlIHdpbGwgc2VlIGxhdGVyLCBwcmUtcHJvY2Vzc2luZyB5b3VyIGRhdGFzZXQgaXMgYW4gaW1wb3J0YW50IHN0ZXAgaW4gZXZhbHVhdGluZyBtb2RlbCBwZXJmb3JtYW5jZSB1c2luZyBjcm9zcy12YWxpZGF0aW9uLgoKYGBge3J9Cmdsb2JhbF9lY29ub215CmBgYAoKCiMgVmlzdWFsaXNlIAoKYGBge3J9Cmdsb2JhbF9lY29ub215ICU+JQogIGZpbHRlcihDb3VudHJ5PT0iU3dlZGVuIikgJT4lCiAgYXV0b3Bsb3QoR0RQKSArCiAgICBnZ3RpdGxlKCJHRFAgZm9yIFN3ZWRlbiIpICsgeWxhYigiJFVTIGJpbGxpb25zIikKYGBgCgoKIyBEZWZpbmUgYSBtb2RlbCAKCkJlZm9yZSBmaXR0aW5nIGEgbW9kZWwgdG8gdGhlIGRhdGEsIHdlIGZpcnN0IG11c3QgZGVzY3JpYmUgdGhlIG1vZGVsLiBUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgdGltZSBzZXJpZXMgbW9kZWxzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGZvcmVjYXN0aW5nLCBhbmQgbXVjaCBvZiB0aGlzIGJvb2sgaXMgZGVkaWNhdGVkIHRvIGRlc2NyaWJpbmcgdmFyaW91cyBtb2RlbHMuIFNwZWNpZnlpbmcgYW4gYXBwcm9wcmlhdGUgbW9kZWwgZm9yIHRoZSBkYXRhIGlzIGVzc2VudGlhbCBmb3IgcHJvZHVjaW5nIGFwcHJvcHJpYXRlIGZvcmVjYXN0cy4KCk1vZGVscyBpbiBSIGFyZSBzcGVjaWZpZWQgdXNpbmcgbW9kZWwgZnVuY3Rpb25zLCB3aGljaCBlYWNoIHVzZSBhIGZvcm11bGEgKHkgfiB4KSBpbnRlcmZhY2UuIFRoZSByZXNwb25zZSB2YXJpYWJsZShzKSBhcmUgc3BlY2lmaWVkIG9uIHRoZSBsZWZ0IG9mIHRoZSBmb3JtdWxhLCBhbmQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgbW9kZWwgaXMgd3JpdHRlbiBvbiB0aGUgcmlnaHQuCgpGb3IgZXhhbXBsZSwgYSB0aW1lIHNlcmllcyBsaW5lYXIgbW9kZWwgdGhhdCBtb2RlbHMgR0RQIGNhbiBiZSBzcGVjaWZpZWQgdXNpbmc6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpUU0xNKEdEUCB+IHRyZW5kKCkpLgpgYGAKCkluIHRoaXMgY2FzZSB0aGUgbW9kZWwgZnVuY3Rpb24gaXMgVFNMTSgpICh0aW1lIHNlcmllcyBsaW5lYXIgbW9kZWwpLCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgR0RQIGFuZCBpdCBpcyBiZWluZyBtb2RlbGxlZCB1c2luZyB0cmVuZCgpIChhIOKAnHNwZWNpYWzigJ0gZnVuY3Rpb24gc3BlY2lmeWluZyBhIGxpbmVhciB0cmVuZCkuCgoKIyBUcmFpbiB0aGUgbW9kZWwgCgpPbmNlIGFuIGFwcHJvcHJpYXRlIG1vZGVsIGlzIHNwZWNpZmllZCwgd2UgbmV4dCB0cmFpbiB0aGUgbW9kZWwgb24gc29tZSBkYXRhLiBPbmUgb3IgbW9yZSBtb2RlbCBzcGVjaWZpY2F0aW9ucyBjYW4gYmUgZXN0aW1hdGVkIHVzaW5nIHRoZSBtb2RlbCgpIGZ1bmN0aW9uLgoKVG8gZXN0aW1hdGUgdGhlIG1vZGVsIGluIG91ciBleGFtcGxlLCB3ZSB1c2U6CgpgYGB7cn0KZml0IDwtIGdsb2JhbF9lY29ub215ICU+JQogIG1vZGVsKHRyZW5kX21vZGVsID0gVFNMTShHRFAgfiB0cmVuZCgpKSkKYGBgCgpUaGlzIGZpdHMgYSBsaW5lYXIgdHJlbmQgbW9kZWwgdG8gdGhlIEdEUCBkYXRhIGZvciBlYWNoIGNvbWJpbmF0aW9uIG9mIGtleSB2YXJpYWJsZXMgaW4gdGhlIHRzaWJibGUuIEluIHRoaXMgZXhhbXBsZSwgaXQgd2lsbCBmaXQgYSBtb2RlbCB0byBlYWNoIG9mIHRoZSAyNjMgY291bnRyaWVzIGluIHRoZSBkYXRhc2V0LiBUaGUgcmVzdWx0aW5nIG9iamVjdCBpcyBhIG1vZGVsIHRhYmxlIG9yIGEg4oCcbWFibGXigJ0uCgpgYGB7cn0KZml0CmBgYAoKRWFjaCByb3cgY29ycmVzcG9uZHMgdG8gb25lIGNvbWJpbmF0aW9uIG9mIHRoZSBrZXkgdmFyaWFibGVzLiBUaGUgdHJlbmRfbW9kZWwgY29sdW1uIGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmaXR0ZWQgbW9kZWwgZm9yIGVhY2ggY291bnRyeS4gCgojIENoZWNrL2V2YWx1YXRlIHlvdXIgbW9kZWwgCgpPbmNlIGEgbW9kZWwgaGFzIGJlZW4gZml0dGVkLCBpdCBpcyBpbXBvcnRhbnQgdG8gY2hlY2sgaG93IHdlbGwgaXQgaGFzIHBlcmZvcm1lZCBvbiB0aGUgZGF0YS4gVGhlcmUgYXJlIHNldmVyYWwgZGlhZ25vc3RpYyB0b29scyBhdmFpbGFibGUgdG8gY2hlY2sgbW9kZWwgYmVoYXZpb3VyLCBhbmQgYWxzbyBhY2N1cmFjeSBtZWFzdXJlcyB0aGF0IGFsbG93IG9uZSBtb2RlbCB0byBiZSBjb21wYXJlZCBhZ2FpbnN0IGFub3RoZXIuCgojIFByb2R1Y2UgZm9yZWNhc3RzCgoKV2l0aCBhbiBhcHByb3ByaWF0ZSBtb2RlbCBzcGVjaWZpZWQsIGVzdGltYXRlZCBhbmQgY2hlY2tlZCwgaXQgaXMgdGltZSB0byBwcm9kdWNlIHRoZSBmb3JlY2FzdHMgdXNpbmcgZm9yZWNhc3QoKS4gVGhlIGVhc2llc3Qgd2F5IHRvIHVzZSB0aGlzIGZ1bmN0aW9uIGlzIGJ5IHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiBmdXR1cmUgb2JzZXJ2YXRpb25zIHRvIGZvcmVjYXN0LiBGb3IgZXhhbXBsZSwgZm9yZWNhc3RzIGZvciB0aGUgbmV4dCAxMCBvYnNlcnZhdGlvbnMgY2FuIGJlIGdlbmVyYXRlZCB1c2luZyBoID0gMTAuIFdlIGNhbiBhbHNvIHVzZSBuYXR1cmFsIGxhbmd1YWdlOyBlLmcuLCBoID0gIjIgeWVhcnMiIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgdHdvIHllYXJzIGludG8gdGhlIGZ1dHVyZS4KCmBgYHtyfQpmaXQgJT4lIGZvcmVjYXN0KGggPSAiMiB5ZWFycyIpCmBgYAoKClRoaXMgaXMgYSBmb3JlY2FzdGluZyB0YWJsZSwgb3Ig4oCcZmFibGXigJ0uIEVhY2ggcm93IGNvcnJlc3BvbmRzIHRvIG9uZSBmb3JlY2FzdCBwZXJpb2QgZm9yIGVhY2ggY291bnRyeS4gVGhlIEdEUCBjb2x1bW4gY29udGFpbnMgdGhlIHBvaW50IGZvcmVjYXN0LCB3aGlsZSB0aGUgLmRpc3RyaWJ1dGlvbiBjb2x1bW4gY29udGFpbnMgdGhlIGZvcmVjYXN0aW5nIGRpc3RyaWJ1dGlvbi4gVGhlIHBvaW50IGZvcmVjYXN0IGlzIHRoZSBtZWFuIChvciBhdmVyYWdlKSBvZiB0aGUgZm9yZWNhc3RpbmcgZGlzdHJpYnV0aW9uLgoKVGhlIGZvcmVjYXN0cyBjYW4gYmUgcGxvdHRlZCBhbG9uZyB3aXRoIHRoZSBoaXN0b3JpY2FsIGRhdGEgdXNpbmcgYXV0b3Bsb3QoKSBhcyBmb2xsb3dzLgoKYGBge3J9CmZpdCAlPiUgZm9yZWNhc3QoaCA9ICIyIHllYXJzIikgJT4lCiAgZmlsdGVyKENvdW50cnk9PSJTd2VkZW4iKSAlPiUKICBhdXRvcGxvdChnbG9iYWxfZWNvbm9teSkgKwogICAgZ2d0aXRsZSgiR0RQIGZvciBTd2VkZW4iKSArIHlsYWIoIiRVUyBiaWxsaW9ucyIpCmBgYAoKCiMjIEZvcmVjYXN0aW5nIG1ldGhvZHMKCjEuIEF2ZXJhZ2UgbWV0aG9kCgpIZXJlLCB0aGUgZm9yZWNhc3RzIG9mIGFsbCBmdXR1cmUgdmFsdWVzIGFyZSBlcXVhbCB0byB0aGUgYXZlcmFnZSAob3Ig4oCcbWVhbuKAnSkgb2YgdGhlIGhpc3RvcmljYWwgZGF0YS4gCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lCiAgbW9kZWwoTUVBTihHRFApKQoKYGBgCgoyLiBOYWl2ZSBtZXRob2QKCkZvciBuYcOvdmUgZm9yZWNhc3RzLCB3ZSBzaW1wbHkgc2V0IGFsbCBmb3JlY2FzdHMgdG8gYmUgdGhlIHZhbHVlIG9mIHRoZSBsYXN0IG9ic2VydmF0aW9uLiBUaGVzZSBhcmUgYWxzbyBrbm93biBhcyByYW5kb20gd2FsayBmb3JlY2FzdHMsIGFzIHRoaXMgbWV0aG9kIGlzIG9wdGltYWwgd2hlbiBkYXRhIGZvbGxvd3MgYSByYW5kb20gd2FsayBtb2RlbC4gVGhpcyBtZXRob2Qgd29ya3MgcmVtYXJrYWJseSB3ZWxsIGZvciBtYW55IGVjb25vbWljIGFuZCBmaW5hbmNpYWwgdGltZSBzZXJpZXMuCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lCiAgbW9kZWwoTkFJVkUoR0RQKSkKYGBgCgozLiBTZWFzb25hbCBuYWl2ZSBtZXRob2QKCkEgc2ltaWxhciBtZXRob2QgaXMgdXNlZnVsIGZvciBoaWdobHkgc2Vhc29uYWwgZGF0YS4gSW4gdGhpcyBjYXNlLCB3ZSBzZXQgZWFjaCBmb3JlY2FzdCB0byBiZSBlcXVhbCB0byB0aGUgbGFzdCBvYnNlcnZlZCB2YWx1ZSBmcm9tIHRoZSBzYW1lIHNlYXNvbiBvZiB0aGUgeWVhciAoZS5nLiwgdGhlIHNhbWUgbW9udGggb2YgdGhlIHByZXZpb3VzIHllYXIpLgoKVGhpcyBsb29rcyBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gaXQgcmVhbGx5IGlzLiBGb3IgZXhhbXBsZSwgd2l0aCBtb250aGx5IGRhdGEsIHRoZSBmb3JlY2FzdCBmb3IgYWxsIGZ1dHVyZSBGZWJydWFyeSB2YWx1ZXMgaXMgZXF1YWwgdG8gdGhlIGxhc3Qgb2JzZXJ2ZWQgRmVicnVhcnkgdmFsdWUuIFdpdGggcXVhcnRlcmx5IGRhdGEsIHRoZSBmb3JlY2FzdCBvZiBhbGwgZnV0dXJlIFEyIHZhbHVlcyBpcyBlcXVhbCB0byB0aGUgbGFzdCBvYnNlcnZlZCBRMiB2YWx1ZSAod2hlcmUgUTIgbWVhbnMgdGhlIHNlY29uZCBxdWFydGVyKS4gU2ltaWxhciBydWxlcyBhcHBseSBmb3Igb3RoZXIgbW9udGhzIGFuZCBxdWFydGVycywgYW5kIGZvciBvdGhlciBzZWFzb25hbCBwZXJpb2RzLgoKYGBge3J9Cmdsb2JhbF9lY29ub215ICU+JQogIG1vZGVsKFNOQUlWRShHRFAgfiBsYWcoInllYXIiKSkpCmBgYAoKCjQuIERyaWZ0IG1ldGhvZAoKQSB2YXJpYXRpb24gb24gdGhlIG5hw692ZSBtZXRob2QgaXMgdG8gYWxsb3cgdGhlIGZvcmVjYXN0cyB0byBpbmNyZWFzZSBvciBkZWNyZWFzZSBvdmVyIHRpbWUsIHdoZXJlIHRoZSBhbW91bnQgb2YgY2hhbmdlIG92ZXIgdGltZSAoY2FsbGVkIHRoZSBkcmlmdCkgaXMgc2V0IHRvIGJlIHRoZSBhdmVyYWdlIGNoYW5nZSBzZWVuIGluIHRoZSBoaXN0b3JpY2FsIGRhdGEuIFRoaXMgaXMgZXF1aXZhbGVudCB0byBkcmF3aW5nIGEgbGluZSBiZXR3ZWVuIHRoZSBmaXJzdCBhbmQgbGFzdCBvYnNlcnZhdGlvbnMsIGFuZCBleHRyYXBvbGF0aW5nIGl0IGludG8gdGhlIGZ1dHVyZS4KCgpgYGB7cn0KZ2xvYmFsX2Vjb25vbXkgJT4lIG1vZGVsKFJXKEdEUCB+IGRyaWZ0KCkpKQpgYGAKCk5vdyBpdCdzIHlvdXIgdHVybjoKCmBgYHtyfQojIFNldCB0cmFpbmluZyBkYXRhIGZyb20gMTk5MiB0byAyMDA2CnRyYWluIDwtIGF1c19wcm9kdWN0aW9uICU+JSBmaWx0ZXJfaW5kZXgoIjE5OTIgUTEiIH4gIjIwMDYgUTQiKQojIEZpdCB0aGUgbW9kZWxzCmJlZXJfZml0IDwtIHRyYWluICU+JQogIG1vZGVsKAogICAgTWVhbiA9IE1FQU4oQmVlciksCiAgICBgTmHDr3ZlYCA9IE5BSVZFKEJlZXIpLAogICAgYFNlYXNvbmFsIG5hw692ZWAgPSBTTkFJVkUoQmVlcikKICApCiMgR2VuZXJhdGUgZm9yZWNhc3RzIGZvciAxNCBxdWFydGVycwpiZWVyX2ZjIDwtIGJlZXJfZml0ICU+JSBmb3JlY2FzdChoPTE0KQojIFBsb3QgZm9yZWNhc3RzIGFnYWluc3QgYWN0dWFsIHZhbHVlcwpiZWVyX2ZjICU+JQogIGF1dG9wbG90KHRyYWluLCBsZXZlbCA9IE5VTEwpICsKICAgIGF1dG9sYXllcihmaWx0ZXJfaW5kZXgoYXVzX3Byb2R1Y3Rpb24sICIyMDA3IFExIiB+IC4pLCBjb2xvciA9ICJibGFjayIpICsKICAgIGdndGl0bGUoIkZvcmVjYXN0cyBmb3IgcXVhcnRlcmx5IGJlZXIgcHJvZHVjdGlvbiIpICsKICAgIHhsYWIoIlllYXIiKSArIHlsYWIoIk1lZ2FsaXRyZXMiKSArCiAgICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRm9yZWNhc3QiKSkKCiMgSW4gdGhpcyBjYXNlLCBvbmx5IHRoZSBzZWFzb25hbCBuYcOvdmUgZm9yZWNhc3RzIGFyZSBjbG9zZSB0byB0aGUgb2JzZXJ2ZWQgdmFsdWVzIGZyb20gMjAwNyBvbndhcmRzLgpgYGAKCgpgYGB7cn0KCgphdXNfcmV0YWlsMSA8LSBhdXNfcmV0YWlsICU+JQogIGZpbHRlcihTdGF0ZSAlaW4lIGMoIk5ldyBTb3V0aCBXYWxlcyIsICJWaWN0b3JpYSIpLAogICAgSW5kdXN0cnkgPT0gIkRlcGFydG1lbnQgc3RvcmVzIikKCmF1c19yZXRhaWwxICU+JQogIG1vZGVsKAogICAgZXRzID0gRVRTKGJveF9jb3goVHVybm92ZXIsIDAuMykpLAogICAgYXJpbWEgPSBBUklNQShsb2coVHVybm92ZXIpKSwKICAgIHNuYWl2ZSA9IFNOQUlWRShUdXJub3ZlcikKICApICU+JQogIGZvcmVjYXN0KGggPSAiMiB5ZWFycyIpICU+JSAKICBhdXRvcGxvdChmaWx0ZXIoYXVzX3JldGFpbCwgeWVhcihNb250aCkgPiAyMDEwKSwgbGV2ZWwgPSBOVUxMKQpgYGAKCmBgYHtyfQpicmlja3MgPC0gYXVzX3Byb2R1Y3Rpb24gJT4lIGZpbHRlcl9pbmRleCgxOTcwIH4gMjAwNCkKYnJpY2tzICU+JSBtb2RlbChNRUFOKEJyaWNrcykpCmJyaWNrcyAlPiUgbW9kZWwoTkFJVkUoQnJpY2tzKSkKYnJpY2tzICU+JSBtb2RlbChTTkFJVkUoQnJpY2tzIH4gbGFnKCJ5ZWFyIikpKQpicmlja3MgJT4lIG1vZGVsKFJXKEJyaWNrcyB+IGRyaWZ0KCkpKQpgYGAKCgp0aGUgbm9uLXNlYXNvbmFsIG1ldGhvZHMgYXJlIGFwcGxpZWQgdG8gR29vZ2xl4oCZcyBkYWlseSBjbG9zaW5nIHN0b2NrIHByaWNlIGluIDIwMTUsIGFuZCB1c2VkIHRvIGZvcmVjYXN0IG9uZSBtb250aCBhaGVhZC4gQmVjYXVzZSBzdG9jayBwcmljZXMgYXJlIG5vdCBvYnNlcnZlZCBldmVyeSBkYXksIHdlIGZpcnN0IHNldCB1cCBhIG5ldyB0aW1lIGluZGV4IGJhc2VkIG9uIHRoZSB0cmFkaW5nIGRheXMgcmF0aGVyIHRoYW4gY2FsZW5kYXIgZGF5cy4KCmBgYHtyfQojIFJlLWluZGV4IGJhc2VkIG9uIHRyYWRpbmcgZGF5cwpnb29nbGVfc3RvY2sgPC0gZ2FmYV9zdG9jayAlPiUKICBmaWx0ZXIoU3ltYm9sID09ICJHT09HIikgJT4lCiAgbXV0YXRlKGRheSA9IHJvd19udW1iZXIoKSkgJT4lCiAgdXBkYXRlX3RzaWJibGUoaW5kZXggPSBkYXksIHJlZ3VsYXIgPSBUUlVFKQojIEZpbHRlciB0aGUgeWVhciBvZiBpbnRlcmVzdApnb29nbGVfMjAxNSA8LSBnb29nbGVfc3RvY2sgJT4lIGZpbHRlcih5ZWFyKERhdGUpID09IDIwMTUpCiMgRml0IHRoZSBtb2RlbHMKZ29vZ2xlX2ZpdCA8LSBnb29nbGVfMjAxNSAlPiUKICBtb2RlbCgKICAgIE1lYW4gPSBNRUFOKENsb3NlKSwKICAgIGBOYcOvdmVgID0gTkFJVkUoQ2xvc2UpLAogICAgRHJpZnQgPSBOQUlWRShDbG9zZSB+IGRyaWZ0KCkpCiAgKQojIFByb2R1Y2UgZm9yZWNhc3RzIGZvciB0aGUgMTkgdHJhZGluZyBkYXlzIGluIEphbnVhcnkgMjAxNQpnb29nbGVfZmMgPC0gZ29vZ2xlX2ZpdCAlPiUgZm9yZWNhc3QoaCA9IDE5KQojIEEgYmV0dGVyIHdheSB1c2luZyBhIHRzaWJibGUgdG8gZGV0ZXJtaW5lIHRoZSBmb3JlY2FzdCBob3Jpem9ucwpnb29nbGVfamFuXzIwMTYgPC0gZ29vZ2xlX3N0b2NrICU+JQogIGZpbHRlcih5ZWFybW9udGgoRGF0ZSkgPT0geWVhcm1vbnRoKCIyMDE2IEphbiIpKQpnb29nbGVfZmMgPC0gZ29vZ2xlX2ZpdCAlPiUgZm9yZWNhc3QoZ29vZ2xlX2phbl8yMDE2KQojIFBsb3QgdGhlIGZvcmVjYXN0cwpnb29nbGVfZmMgJT4lCiAgYXV0b3Bsb3QoZ29vZ2xlXzIwMTUsIGxldmVsID0gTlVMTCkgKwogICAgYXV0b2xheWVyKGdvb2dsZV9qYW5fMjAxNiwgQ2xvc2UsIGNvbG9yPSdibGFjaycpICsKICAgIGdndGl0bGUoIkdvb2dsZSBzdG9jayAoZGFpbHkgZW5kaW5nIDMxIERlYyAyMDE1KSIpICsKICAgIHhsYWIoIkRheSIpICsgeWxhYigiQ2xvc2luZyBQcmljZSAoVVMkKSIpICsKICAgIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJGb3JlY2FzdCIpKQpgYGAKCgpTb21ldGltZXMgb25lIG9mIHRoZXNlIHNpbXBsZSBtZXRob2RzIHdpbGwgYmUgdGhlIGJlc3QgZm9yZWNhc3RpbmcgbWV0aG9kIGF2YWlsYWJsZTsgYnV0IGluIG1hbnkgY2FzZXMsIHRoZXNlIG1ldGhvZHMgd2lsbCBzZXJ2ZSBhcyBiZW5jaG1hcmtzIHJhdGhlciB0aGFuIHRoZSBtZXRob2Qgb2YgY2hvaWNlLiBUaGF0IGlzLCBhbnkgZm9yZWNhc3RpbmcgbWV0aG9kcyB3ZSBkZXZlbG9wIHdpbGwgYmUgY29tcGFyZWQgdG8gdGhlc2Ugc2ltcGxlIG1ldGhvZHMgdG8gZW5zdXJlIHRoYXQgdGhlIG5ldyBtZXRob2QgaXMgYmV0dGVyIHRoYW4gdGhlc2Ugc2ltcGxlIGFsdGVybmF0aXZlcy4gSWYgbm90LCB0aGUgbmV3IG1ldGhvZCBpcyBub3Qgd29ydGggY29uc2lkZXJpbmcuCgoKCgoKIyMjIEFDRiBhbmQgUEFDRiBwbG90cwoKV2UgY2FuIGFsc28gc2VlIGhvdyBzZWFzb25hbCBvdXIgZGF0YSBpcyBieSBsb29raW5nIGF0IEFDRiBhbmQgUEFDRiBwbG90cy4gCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojIGFjZiBwbG90cyAKaG9saWRheXMgJT4lIAogIEFDRihUcmlwcykgJT4lCiAgYXV0b3Bsb3QoKQoKCmhvbGlkYXlzICU+JSAKICBQQUNGKFRyaXBzLDQpICU+JQogIGF1dG9wbG90KCkKCmBgYApIZXJlLCB0aGUgbG93IHNlYXNvbmFsaXR5IGluIHRoZSBBQ1QgaXMgZXZpZGVudCBjb21wYXJlZCB0byB0aGUgb3RoZXIgc3RhdGVzLgoKCkZpbmFsbHksIHdlIHNob3cgYSBjb21wb3NpdGUgcGxvdCBjcmVhdGVkIHVzaW5nIGBnZ190c2Rpc3BsYXkoKWAuIFRoaXMgaXMgYSBsaXR0bGUgZGlmZmVyZW50IGZyb20gdGhlIGNvcnJlc3BvbmRpbmcgYGdndHNkaXNwbGF5KClgIGZ1bmN0aW9uIGluIHRoZSBmb3JlY2FzdCBwYWNrYWdlIHdoaWNoIHNob3dlZCB0aGUgUEFDRiBpbiB0aGUgYm90dG9tIHJpZ2h0IHBhbmVsIGJ5IGRlZmF1bHQuIEkgdGhpbmsgdGhlIHNlYXNvbiBwbG90IGlzIGEgbGl0dGxlIG1vcmUgaW5mb3JtYXRpdmUgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMsIHNvIHRoYXQgaXMgd2hhdCBpcyBzaG93biBieSBkZWZhdWx0IGluIHRoaXMgbmV3IGZ1bmN0aW9uLiBUaGUgb3RoZXIgcGFuZWxzIGFyZSB0aGUgc2FtZS4KCmBgYHtyfQpob2xpZGF5cyAlPiUgCiAgZmlsdGVyKFN0YXRlPT0iVGFzbWFuaWEiKSAlPiUgCiAgZ2dfdHNkaXNwbGF5KFRyaXBzKQpgYGAKCg==